package hyl.core.net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import hyl.core.run.IReadyT1;
import hyl.core.run.MyRun;

public class NioServer implements Runnable {
	ServerSocketChannel _sschannel = null;
	Selector _select = null;
	static int I连接超时 = 3000;
	static int I并发连接数 = 100;

	public static int getI连接超时() {
		return I连接超时;
	}

	public static void setI连接超时(int i连接超时) {
		I连接超时 = i连接超时;
	}

	public static int getI并发连接数() {
		return I并发连接数;
	}

	public static void setI并发连接数(int i连接数) {
		I并发连接数 = i连接数;
	}

	public NioServer(int port) throws IOException {
		ini(new InetSocketAddress(port));
	}

	IReadyT1<NioReader> _接收器 = null;

	public IReadyT1<NioReader> get接收器() {
		return _接收器;
	}

	public void set接收器(IReadyT1<NioReader> handler) {
		this._接收器 = handler;
	}

	protected void ini(SocketAddress sa) {
		try {
			/* 创建选择器 */
			_select = Selector.open();

			/* 创建服务器通道 */
			_sschannel = ServerSocketChannel.open();

			/* 调整此通道的阻塞模式。如果为 true，则此通道将被置于阻塞模式；如果为 false，则此通道将被置于非阻塞模式 */
			_sschannel.configureBlocking(false);

			/* 设置监听服务器的端口，设置最大连接缓冲数为100 */
			_sschannel.bind(sa, I并发连接数);

			/* 服务器通道只能对tcp链接事件感兴趣 */
			_sschannel.register(_select, SelectionKey.OP_ACCEPT);

			System.out.println("服务器开启监听地址 : " + sa);

		} catch (IOException e1) {
			System.out.println("服务器开启失败");
			return;
		}
	}

	public void run() {

		/* 服务器线程被中断后会退出 */
		try {
			// 轮询select 中的列表
			while (!Thread.currentThread().isInterrupted()) {
				// 等待某个信道就绪
				if (_select.select(I连接超时) == 0) {
					// System.out.println(".");
					continue;
				}
				// 获得就绪信道的键迭代器
				Iterator<SelectionKey> keyIter = _select.selectedKeys().iterator();
				SelectionKey key = null;
				// 使用迭代器进行遍历就绪信道
				// int i=0;
				// keyIter 只要有客户端接入,就开始无限循环
				while (keyIter.hasNext()) {
					key = keyIter.next();
					/* 防止下次select方法返回已处理过的通道 */

					try {
						// 这种情况是有客户端连接过来,准备一个clientChannel与之通信
						if (key.isAcceptable()) {
							NioReader attachbuffer = null;
							if (_接收器 != null) {
								attachbuffer = this._接收器.ready();// 准备一个缓冲对象 ,用来 读取数据
								SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
								sc.configureBlocking(false);
								sc.register(_select, SelectionKey.OP_READ, attachbuffer);

							}
						}
						// 客户端有写入时
						/* （普通）通道感兴趣读事件且有数据可读 */
						else if (key.isReadable()) {
							NioReader read = (NioReader) key.attachment();
							if (read != null)
								read.read(key);

						}
					} catch (IOException e) {
						key.cancel();
						key.channel().close();
					}
					keyIter.remove();
				}
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	/**
	 * 发送给连接服务端口的所有人
	 * 
	 * @param 数据包
	 * @return 接收人数
	 * @throws IOException
	 */
	public int sendAll(ByteBuffer 数据包) {
		return sendEx(null, 数据包);
	}

	/**
	 * 发送给出来 排除
	 * 
	 * @param 数据包
	 * @return 接收人数
	 * @throws IOException
	 */
	public int sendEx(SocketChannel 排除他, ByteBuffer 数据包) {
		if (数据包 != null && _select == null)
			return 0;
		int count = 0;
		Channel 排除 = (Channel) 排除他;
		try {
			if (_select.selectNow() == 0) {
				return 0;
			}
		} catch (IOException e1) {
			return count;
		}
		try {

			// 广播数据到所有的SocketChannel中
			for (SelectionKey key : _select.keys()) {
				if (!key.isValid())
					continue;
				Channel targetchannel = key.channel();
				// 如果except不为空，不回发给发送此内容的客户端

				if (targetchannel instanceof SocketChannel && targetchannel != 排除) {
					SocketChannel dest = (SocketChannel) targetchannel;
					dest.write(数据包);
					count++;
				}
			}
			return count;
		} catch (Exception e) {
			return count;
		}
	}

	public void close() {
		// TODO Auto-generated method stub
		try {
			_select.close();
			_sschannel.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	static MyRun _mr;

	public static void startServer(NioServer srv) {
		if (_mr == null)
			_mr = MyRun.getInstance("服务端", 0);
		_mr.execute(srv);

	}

	public static void exit() {

		_mr.close();

	}
}
